/**
 * \file vector4.hpp
 * 
 * \section COPYRIGHT
 *
 * Basis: A 3D Mathematics Library
 *
 * ---------------------------------------------------------------------
 *
 * Copyright (c) 2010, Don Olmstead
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *
 *  3. Neither the name of organization nor the names of its contributors may be
 *     used to endorse or promote products derived from this software without
 *     specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef BASIS_VECTOR4_HPP_INCLUDED
#define BASIS_VECTOR4_HPP_INCLUDED

#include <basis/scalar.hpp>

namespace basis
{
	/**
	 * A vector containing four components.
	 *
	 * \tparam Real A real number type.
	 * \tparam Rep The internal storage type.
	 *
	 * \author Don Olmstead
	 * \version 0.1
	 */
	template <typename Real, typename Rep>
	class vector4
	{
		public:

			//------------------------------------------------------------
			// Member types
			//------------------------------------------------------------

			/// A real number type
			typedef Real value_type;
			/// Reference to a real number type
			typedef Real& reference;
			/// Unsigned integral type
			typedef std::size_t size_type;
			/// The internal storage type
			typedef Rep simd_type;
			/// A scalar type stored in a vector
			typedef typename scalar<Real, Rep> scalar_type;

			//------------------------------------------------------------
			// Construction/Destruction
			//------------------------------------------------------------

			/**
			 * Creates an instance of the vector4 class.
			 *
			 * Left uninitialized to speed construction.
			 */
			inline vector4()
			{ }

			/**
			 * Initializes an instance of the vector4 class.
			 *
			 * \param x The x component.
			 * \param y The y component.
			 * \param z The z component.
			 * \param w The w component.
			 */
			inline vector4(value_type x, value_type y, value_type z, value_type w)
				: _rep(x, y, z, w)
			{ }

			/**
			 * Initializes an instance of the vector4 class.
			 *
			 * \param values An array containing the values for the components.
			 */
			inline vector4(const value_type* values)
				: _rep(values)
			{ }

			/**
			 * Initializes an instance of the vector4 class.
			 *
			 * \param copy The instance to copy.
			 */
			inline vector4(const vector4& copy)
				: _rep(copy._rep)
			{ }

			//------------------------------------------------------------
			// Assignment
			//------------------------------------------------------------

			/**
			 * Assignment operator for the vector4 class.
			 *
			 * \param copy The instance to copy.
			 */
			inline vector4& operator= (const vector4& copy)
			{
				_rep = copy._rep;
				
				return *this;
			}

			//------------------------------------------------------------
			// Element access
			//------------------------------------------------------------

			/**
			 * Accessor for the x component of the vector.
			 *
			 * \returns The x component of the vector.
			 */
			inline const scalar_type x() const
			{
				return scalar_type(_rep.shuffle<0, 0, 0, 0>());
			}

			/**
			 * Accessor for the x component of the vector.
			 *
			 * \returns The x component of the vector.
			 */
			inline reference x()
			{
				return _rep[0];
			}

			/**
			 * Accessor for the y component of the vector.
			 *
			 * \returns The y component of the vector.
			 */
			inline const scalar_type y() const
			{
				return scalar_type(_rep.shuffle<1, 1, 1, 1>());
			}

			/**
			 * Accessor for the y component of the vector.
			 *
			 * \returns The y component of the vector.
			 */
			inline reference y()
			{
				return _rep[1];
			}
			
			/**
			 * Accessor for the z component of the vector.
			 *
			 * \returns The z component of the vector.
			 */
			inline const scalar_type z() const
			{
				return scalar_type(_rep.shuffle<2, 2, 2, 2>());
			}

			/**
			 * Accessor for the z component of the vector.
			 *
			 * \returns The z component of the vector.
			 */
			inline reference z()
			{
				return _rep[2];
			}

			/**
			 * Accessor for the w component of the vector.
			 *
			 * \returns The w component of the vector.
			 */
			inline const scalar_type w() const
			{
				return scalar_type(_rep.shuffle<3, 3, 3, 3>());
			}

			/**
			 * Accessor for the w component of the vector.
			 *
			 * \returns The w component of the vector.
			 */
			inline reference w()
			{
				return _rep[3];
			}

			/**
			 * Sets all the values of the vector.
			 *
			 * \param x The x component.
			 * \param y The y component.
			 * \param z The z component.
			 * \param w The w component.
			 */
			inline void set_values(value_type x, value_type y, value_type z, value_type w)
			{
				_rep.set_values(x, y, z, w);
			}

			/**
			 * Accessor for the size of the vector4.
			 *
			 * Size is the number of elements that can be accessed
			 * within the type.
			 *
			 * \returns The size of the vector4.
			 */
			inline static size_type size()
			{
				return 4;
			}

			/**
			 * Accessor for the capacity of the vector4.
			 *
			 * Capacity is the number of elements that could be
			 * stored in the type.
			 *
			 * \returns The capacity of the vector4.
			 */
			inline static size_type capacity()
			{
				return Rep::capacity();
			}

			//------------------------------------------------------------
			// Equality operators
			//------------------------------------------------------------

			/**
			 * Tests vectors for equality.
			 *
			 * \param rhs Source vector.
			 * \returns \b true if the vectors are equal; \b false otherwise.
			 */
			inline bool operator== (const vector4& rhs) const
			{
				return Rep::equal(_rep, rhs._rep) == Rep::mask_all();
			}

			/**
			 * Tests vectors for inequality.
			 *
			 * \param rhs Source vector.
			 * \returns \b true if the vectors are not equal; \b false otherwise.
			 */
			inline bool operator!= (const vector4& rhs) const
			{
				return Rep::not_equal(_rep, rhs._rep) == Rep::mask_all();
			}

			//------------------------------------------------------------
			// Arithmetic operators
			//------------------------------------------------------------

			/**
			 * Returns a vector pointing in the opposite direction.
			 *
			 * \returns A vector pointing in the opposite direction.
			 */
			inline const vector4 operator- () const
			{
				return vector4(-_rep);
			}

			/**
			 * Adds two vectors.
			 *
			 * \param rhs Source vector.
			 * \returns Result of the addition.
			 */
			inline const vector4 operator+ (const vector4& rhs) const
			{
				return vector4(_rep + rhs._rep);
			}

			/**
			 * Subtracts a vector from a vector.
			 *
			 * \param rhs Source vector.
			 * \returns Result of the subtraction.
			 */
			inline const vector4 operator- (const vector4& rhs) const
			{
				return vector4(_rep - rhs._rep);
			}

			/**
			 * Multiplies the components of two vectors by each other.
			 *
			 * \param rhs Source vector.
			 * \returns Result of the multiplication.
			 */
			inline const vector4 operator* (const vector4& rhs) const
			{
				return vector4(_rep * rhs._rep);
			}

			/**
			 * Divides the components of two vectors by each other.
			 *
			 * \param rhs Source vector.
			 * \returns Result of the division.
			 */
			inline const vector4 operator/ (const vector4& rhs) const
			{
				return vector4(_rep / rhs._rep);
			}

			/**
			 * Multiplies a vector by a scalar value.
			 *
			 * \param rhs Source value.
			 * \returns Result of the multiplication.
			 */
			inline const vector4 operator* (const scalar_type& rhs) const
			{
				return vector4(_rep * rhs._rep);
			}

			/**
			 * Divides a vector by a scalar value.
			 *
			 * \param rhs Source value.
			 * \returns Result of the division.
			 */
			inline const vector4 operator/ (const scalar_type& rhs) const
			{
				return vector4(_rep / rhs._rep);
			}

			/**
			 * Adds two vectors.
			 *
			 * \param rhs Source vector.
			 * \returns Result of the addition.
			 */
			inline vector4& operator+= (const vector4& rhs)
			{
				_rep += rhs._rep;

				return *this;
			}

			/**
			 * Subtracts a vector from a vector.
			 *
			 * \param rhs Source vector.
			 * \returns Result of the subtraction.
			 */
			inline vector4& operator-= (const vector4& rhs)
			{
				_rep -= rhs._rep;

				return *this;
			}

			/**
			 * Multiplies the components of two vectors by each other.
			 *
			 * \param rhs Source vector.
			 * \returns Result of the multiplication.
			 */
			inline vector4& operator*= (const vector4& rhs)
			{
				_rep *= rhs._rep;

				return *this;
			}

			/**
			 * Divides the components of two vectors by each other.
			 *
			 * \param rhs Source vector.
			 * \returns Result of the division.
			 */
			inline vector4& operator/= (const vector4& rhs)
			{
				_rep /= rhs._rep;

				return *this;
			}

			/**
			 * Multiplies a vector by a scalar value.
			 *
			 * \param rhs Source value.
			 * \returns Result of the multiplication.
			 */
			inline vector4& operator*= (const scalar_type& rhs)
			{
				_rep *= rhs._rep;

				return *this;
			}

			/**
			 * Divides a vector by a scalar value.
			 *
			 * \param rhs Source value.
			 * \returns Result of the division.
			 */
			inline vector4& operator/= (const scalar_type& rhs)
			{
				_rep /= rhs._rep;

				return *this;
			}

			//------------------------------------------------------------
			// Arithmetic operations
			//------------------------------------------------------------

			/**
			 * Returns a vector that contains the lowest value from each matching pair of components.
			 *
			 * \param value1 Source vector.
			 * \param value2 Source vector.
			 * \returns A vector contains the lowest value from each matching pair of components.
			 */
			inline static const vector4 min(const vector4& value1, const vector4& value2)
			{
				return vector4(Rep::min(value1._rep, value2._rep));
			}

			/**
			 * Returns a vector that contains the highest value from each matching pair of components.
			 *
			 * \param value1 Source vector.
			 * \param value2 Source vector.
			 * \returns A vector contains the highest value from each matching pair of components.
			 */
			inline static const vector4 max(const vector4& value1, const vector4& value2)
			{
				return vector4(Rep::max(value1._rep, value2._rep));
			}

			/**
			 * Restricts a value to be within a specified range.
			 *
			 * \param value The vector to clamp.
			 * \param minimum The minimum value.
			 * \param maximum The maximum value.
			 * \returns The clamped value.
			 */
			inline static const vector4 clamp(const vector4& value, const vector4& minimum, const vector4& maximum)
			{
				return vector4(Rep::max(minimum._rep, Rep::min(maximum._rep, value._rep)));
			}

			//------------------------------------------------------------
			// Vector operations
			//------------------------------------------------------------

			/**
			 * Calculates the dot product of two vectors.
			 *
			 * If the two vectors are unit vectors, the dot product returns a floating
			 * point value between -1 and 1 that can be used to determine some properties
			 * of the angle between two vectors. For example, it can show whether the 
			 * vectors are orthogonal, parallel, or have an acute or obtuse angle between them.
			 *
			 * \param value1 Source vector.
			 * \param value2 Source vector.
			 * \returns The dot product of two vectors.
			 */
			inline static const scalar_type dot(const vector4& value1, const vector4& value2)
			{
				return scalar_type(simd_type::dot4(value1._rep, value2._rep));
			}

			/**
			 * Calculates the length of the vector.
			 *
			 * \returns The length of the vector.
			 */
			inline const scalar_type length() const
			{
				return scalar_type(simd_type::sqrt(simd_type::dot4(_rep, _rep)));
			}

			/**
			 * Calculates the length of the vector squared.
			 *
			 * \returns The length of the vector squared.
			 */
			inline const scalar_type length_squared() const
			{
				return scalar_type(simd_type::dot4(_rep, _rep));
			}

			/**
			 * Calculates the distance between two vectors.
			 *
			 * \param value1 Source vector.
			 * \param value2 Source vector.
			 * \returns The distance between two vectors.
			 */
			inline static const scalar_type distance(const vector4& value1, const vector4& value2)
			{
				vector4 difference = value1 - value2;

				return difference.length();
			}

			/**
			 * Calculates the distance squared between two vectors.
			 *
			 * \param value1 Source vector.
			 * \param value2 Source vector.
			 * \returns The distance squared between two vectors.
			 */
			inline static const scalar_type distance_squared(const vector4& value1, const vector4& value2)
			{
				vector4 difference = value1 - value2;

				return difference.length_squared();
			}

			/**
			 * Turns the current vector into a unit vector.
			 *
			 * The result is a vector one unit in length pointing in the same direction
			 * as the original vector.
			 */
			inline void normalize()
			{
				_rep *= simd_type::inv_sqrt(simd_type::dot4(_rep, _rep));
			}

			/**
			 * Creates a unit vector from the specified vector.
			 *
			 * The result is a vector one unit in length pointing in the same direction
			 * as the original vector.
			 *
			 * \param value Source vector.
			 * \returns The unit vector.
			 */
			inline static const vector4 normalize(const vector4& value)
			{
				return vector4(value._rep * simd_type::inv_sqrt(simd_type::dot4(value._rep, value._rep)));
			}

			//------------------------------------------------------------
			// Shuffle operation
			//------------------------------------------------------------

			/**
			 * Shuffles the values of the vector.
			 *
			 * \tparam _0 The element to move to the 1st slot.
			 * \tparam _1 The element to move to the 2nd slot.
			 * \tparam _2 The element to move to the 3rd slot.
			 * \tparam _3 The element to move to the 4th slot.
			 *
			 * \returns The reordered vector.
			 */
			template <size_type _0, size_type _1, size_type _2, size_type _3>
			inline const vector4 shuffle() const
			{
				return vector4(_rep.shuffle<_0, _1, _2, _3>());
			}

			//------------------------------------------------------------
			// Standard vectors
			//------------------------------------------------------------

			/**
			 * Returns the x unit vector (1, 0, 0, 0).
			 *
			 * \returns The x unit vector (1, 0, 0, 0).
			 */
			inline static const vector4 unit_x()
			{
				return vector4((value_type)1, (value_type)0, (value_type)0, (value_type)0);
			}

			/**
			 * Returns the y unit vector (0, 1, 0, 0).
			 *
			 * \returns The y unit vector (0, 1, 0, 0).
			 */
			inline static const vector4 unit_y()
			{
				return vector4((value_type)0, (value_type)1, (value_type)0, (value_type)0);
			}

			/**
			 * Returns the z unit vector (0, 0, 1, 0).
			 *
			 * \returns The z unit vector (0, 0, 1, 0).
			 */
			inline static const vector4 unit_z()
			{
				return vector4((value_type)0, (value_type)0, (value_type)1, (value_type)0);
			}

			/**
			 * Returns the w unit vector (0, 0, 0, 1).
			 *
			 * \returns The w unit vector (0, 0, 0, 1).
			 */
			inline static const vector4 unit_w()
			{
				return vector4((value_type)0, (value_type)0, (value_type)0, (value_type)1);
			}

			/**
			 * Returns a vector with all components set to one.
			 *
			 * \returns A vector with all components set to one.
			 */
			inline static const vector4 one()
			{
				return vector4((value_type)1, (value_type)1, (value_type)1, (value_type)1);
			}

			/**
			 * Returns a vector with all components set to zero.
			 *
			 * \returns A vector with all components set to zero.
			 */
			inline static const vector4 zero()
			{
				return vector4((value_type)0, (value_type)0, (value_type)0, (value_type)0);
			}

		private:

			friend class scalar<Real, Rep>;

			/**
			 * Initializes an instance of the vector4 class.
			 *
			 * \param rep Internal representation to use.
			 */
			inline vector4(const simd_type& rep)
				: _rep(rep)
			{ }

			/// Internal representation of the type
			simd_type _rep;

	} ; // end class vector4

	/**
	 * Outputs a vector to a stream.
	 *
	 * \tparam Real A real number type.
	 * \tparam Rep The internal storage type.
	 *
	 * \param out The stream to write to.
	 * \param vector The vector to output to.
	 * \returns The stream after being written to.
	 */
	template <typename Real, typename Rep>
	std::ostream& operator<< (std::ostream& out, const vector4<Real, Rep>& vector)
	{
		out << "("  << vector.x()
			<< ", " << vector.y()
			<< ", " << vector.z()
			<< ", " << vector.w() << ")";

		return out;
	}

} // end namespace basis

//------------------------------------------------------------
// Swizzle defines
//------------------------------------------------------------

#include <basis/detail/vector4_swizzle.hpp>

#endif // end BASIS_VECTOR4_HPP_INCLUDED
